package benchmarks.tpch.mongo;

import api.Configuration;
import api.connection.ConnectionMongo;
import api.connection.DBConnection;
import api.result.Result;
import benchmarks.tpch.TPCHLoader;
import com.mongodb.*;

import java.io.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Scanner;


public class TPCHLoaderMongo extends TPCHLoader {
    private static String DATABASE_NAME = "tpchdata";
    private static String PATH_NAME = "dataset\\tpch0.01";
    private DBConnection<MongoClient> dbConnection;
    private static MongoClient mongoClient;

    private final static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    public TPCHLoaderMongo(double scaleFactor) {
        super(scaleFactor);
    }

    public void setDatabaseName(String databaseName){
        this.DATABASE_NAME = databaseName;
    }

    public void setPathName(String pathName){
        this.PATH_NAME = pathName;
    }

    public static final List<String> MAP_CUSTOMER = Arrays.asList(
            "custkey",
            "name",
            "address",
            "nationkey",
            "phone",
            "acctbal",
            "mktsegment",
            "comment");
    public static final List<String> MAP_SUPPLIER = Arrays.asList(
            "suppkey",
            "name",
            "address",
            "nationkey",
            "phone",
            "acctbal",
            "comment");
    public static final List<String> MAP_LINEITEM = Arrays.asList(
            "orderkey",
            "partkey",
            "suppkey",
            "linenumber",
            "quantity",
            "extendedprice",
            "discount",
            "tax",
            "returnflag",
            "linestatus",
            "shipdate",
            "commitdate",
            "receiptdate",
            "shipinstruct",
            "shipmode",
            "comment");
    public static final List<String> MAP_NATION = Arrays.asList(
            "nationkey",
            "name",
            "regionkey",
            "comment");
    public static final List<String> MAP_ORDERS = Arrays.asList(
            "orderkey",
            "custkey",
            "orderstatus",
            "totalprice",
            "orderdate",
            "orderpriority",
            "clerk",
            "shippriority",
            "comment");
    public static final List<String> MAP_PART = Arrays.asList(
            "partkey",
            "name",
            "mfgr",
            "brand",
            "type",
            "size",
            "container",
            "retailprice",
            "comment");
    public static final List<String> MAP_PARTSUPP = Arrays.asList(
            "partkey",
            "suppkey",
            "availqty",
            "supplycost",
            "comment");
    public static final List<String> MAP_REGION = Arrays.asList(
            "regionkey",
            "name",
            "comment");

    public static void createIndex(){
        DB database = mongoClient.getDB(DATABASE_NAME);
        DBCollection customer = database.getCollection("customer");
        customer.createIndex("custkey");

        DBCollection supplier = database.getCollection("supplier");
        supplier.createIndex("suppkey");

        DBCollection nation = database.getCollection("nation");
        nation.createIndex("nationkey");

        DBCollection orders = database.getCollection("orders");
        orders.createIndex("orderkey");

        DBCollection part = database.getCollection("part");
        part.createIndex("partkey");

        DBCollection partsupp = database.getCollection("partsupp");
        partsupp.createIndex("partkey");
        partsupp.createIndex("suppkey");

        DBCollection region = database.getCollection("region");
        region.createIndex("regionkey");

        System.out.println("Successfully Creat Index!");
    }

    public static Object autoCast(String entry){
        try{
            int asInt = Integer.valueOf(entry);
            return asInt;
        }catch(NumberFormatException e){
            try{
                double asDouble = Double.valueOf(entry);
                return asDouble;
            }catch(Exception e1){
                try{
                    if(entry.matches("[0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9]-"
                            + "[0-9][0-9][0-9][0-9]")){
                        return entry;
                    }else{
                        Date date = (Date) sdf.parse(entry);
                        return date;
                    }
                }catch(ParseException e2){
                    return entry;
                }
            }
        }
    }

    public static void CreateDealsCollection(){
        DB database = mongoClient.getDB(DATABASE_NAME);
        DBCollection collection = database.getCollection("deals");
        collection.drop();
        DBCollection lineitem = database.getCollection("lineitem");
        DBCursor cursor = lineitem.find();
        //int i = 0;
        while(cursor.hasNext()){
            DBObject obj = cursor.next();
            updateLineitem(database, obj);
            collection.insert(obj);
            //i++;
            //System.out.println("Insert Successfully " + i);
        }
        System.out.println("Successfully Created \"deals collection\" !");
    }

    public static void updateLineitem(DB database, DBObject obj){
        BasicDBObject query1 = new BasicDBObject();
        query1.put("orderkey", obj.get("orderkey"));
        DBCollection orders = database.getCollection("orders");
        DBObject orderObj = orders.findOne(query1);
        updateOrders(database, orderObj);
        obj.put("orderkey", orderObj);

        BasicDBObject query2 = new BasicDBObject("partkey", obj.get("partkey")).append("suppkey", obj.get("suppkey"));
        //System.out.println(obj.get("partkey") +" " + obj.get("suppkey"));
        DBCollection partsupp = database.getCollection("partsupp");
        DBObject partsuppObj = partsupp.findOne(query2);
        //System.out.println(partsuppObj.get("partkey") +" " + partsuppObj.get("suppkey"));
        updatePartsupp(database, partsuppObj);
        obj.put("partsupp", partsuppObj);
        obj.removeField("partkey");
        obj.removeField("suppkey");
    }

    public static void updateOrders(DB database, DBObject obj){
        BasicDBObject query1 = new BasicDBObject();
        query1.put("custkey", obj.get("custkey"));
        DBCollection customer = database.getCollection("customer");
        DBObject custObj = customer.findOne(query1);
        updateCustomer(database, custObj);
        obj.put("custkey", custObj);
        //System.out.println("Successfully updated!");
    }

    public static void updatePartsupp(DB database, DBObject obj){
        BasicDBObject query1 = new BasicDBObject();
        query1.put("partkey", obj.get("partkey"));
        DBCollection part = database.getCollection("part");
        DBObject partObj = part.findOne(query1);
        obj.put("partkey", partObj);

        BasicDBObject query2 = new BasicDBObject();
        query2.put("suppkey", obj.get("suppkey"));
        DBCollection supplier = database.getCollection("supplier");
        DBObject suppObj = supplier.findOne(query2);
        updateSupplier(database, suppObj);
        obj.put("suppkey", suppObj);
    }

    public static void updateCustomer(DB database, DBObject obj){
        BasicDBObject query1 = new BasicDBObject();
        query1.put("nationkey", obj.get("nationkey"));
        DBCollection part = database.getCollection("nation");
        DBObject nationObj = part.findOne(query1);
        updateNation(database, nationObj);
        obj.put("nationkey", nationObj);
    }

    public static void updateSupplier(DB database, DBObject obj){
        BasicDBObject query1 = new BasicDBObject();
        query1.put("nationkey", obj.get("nationkey"));
        DBCollection part = database.getCollection("nation");
        DBObject nationObj = part.findOne(query1);
        updateNation(database, nationObj);
        obj.put("nationkey", nationObj);
    }

    public static void updateNation(DB database, DBObject obj){
        BasicDBObject query1 = new BasicDBObject();
        query1.put("regionkey", obj.get("regionkey"));
        DBCollection part = database.getCollection("region");
        DBObject regionObj = part.findOne(query1);
        obj.put("regionkey", regionObj);
    }

    public Result loadData(DBConnection connection) {
        this.dbConnection = (ConnectionMongo)connection;
        this.mongoClient = dbConnection.makeConnection();
        DB db = this.mongoClient.getDB(DATABASE_NAME);
        File folder = new File(PATH_NAME);
        {
            FilenameFilter filter = new FilenameFilter(){
                public boolean accept(File arg0, String arg1){
                    return arg1.endsWith(".tbl");
                }
            };
            for(String fileName : folder.list(filter)){
                File toRead = new File(folder, fileName);
                String collName;
                List<String> keys;
                if (fileName.startsWith("customer")) {
                    keys = MAP_CUSTOMER;
                    collName = "customer";
                } else if (fileName.startsWith("lineitem")) {
                    keys = MAP_LINEITEM;
                    collName = "lineitem";
                } else if (fileName.startsWith("nation")) {
                    keys = MAP_NATION;
                    collName = "nation";
                } else if (fileName.startsWith("order")) {
                    keys = MAP_ORDERS;
                    collName = "orders";
                } else if (fileName.startsWith("part.")) {
                    keys = MAP_PART;
                    collName = "part";
                } else if (fileName.startsWith("partsupp")) {
                    keys = MAP_PARTSUPP;
                    collName = "partsupp";
                } else if (fileName.startsWith("region")) {
                    keys = MAP_REGION;
                    collName = "region";
                } else if (fileName.startsWith("supplier")) {
                    keys =  MAP_SUPPLIER;
                    collName = "supplier";
                } else {
                    System.out.println("unmappable file " + fileName);
                    System.out.println("process stopped");
                    break;
                }
                try{
                    DBCollection collection = db.getCollection(collName);
                    collection.drop();
                    @SuppressWarnings("resource")
                    Scanner scanner = new Scanner(toRead);
                    while(scanner.hasNext()){
                        String line = scanner.nextLine();
                        DBObject mongoEntry = new BasicDBObject();
                        String[] elements = line.split("\\|");

                        for(int i = 0; i < elements.length; i++){
                            mongoEntry.put(keys.get(i), autoCast(elements[i]));
                        }
                        collection.insert(mongoEntry);
                    }
                }catch(FileNotFoundException e){
                    e.printStackTrace();
                }
                //System.out.println("Sucessfully imported!");
            }
            createIndex();

            CreateDealsCollection();


        }
        return null;
    }

    private static class TestLoadData{
        public static void main(String[] args){
            Configuration config = new Configuration();
            config.setBenchmark("tpchdata");
            config.setDb_username("root");
            config.setDb_password("123456");
            DBConnection DBconnect = new ConnectionMongo(config);
            TPCHLoaderMongo tpchLoaderMongo = new TPCHLoaderMongo(1);
            tpchLoaderMongo.setDatabaseName("tpchdata");
            tpchLoaderMongo.setPathName("d:\\data\\dbgen");
            tpchLoaderMongo.loadData(DBconnect);
        }
    }
}
